﻿#include "SignalManager.h"
#include "Logger.h"

#include <utility>
#include <exception>
#include <map>
#include <unordered_map>
#include <mutex>

ETERFREE_SPACE_BEGIN

using Handler = SignalManager::Handler;
using Strategy = SignalManager::Strategy;

using IDType = SignalManager::IDType;
using Observer = SignalManager::Observer;
using PoolType = std::map<IDType, Observer>;

static std::mutex _handleMutex;
static std::unordered_map<int, Handler> _handleMapper;

static std::mutex _strategyMutex;
static std::unordered_map<int, Strategy> _strategyMapper;

static std::mutex _poolMutex;
static std::map<int, PoolType> _poolMapper;

void SignalManager::handle(int _signal)
{
	std::lock_guard lock(_poolMutex);
	if (auto iterator = _poolMapper.find(_signal); \
		iterator != _poolMapper.end())
		for (auto& [_, observer] : iterator->second)
		{
			try
			{
				observer(_signal);
			}
			catch (std::exception& exception)
			{
				Logger::output(Logger::Level::ERROR, \
					std::source_location::current(), \
					exception);
			}
		}

	auto strategy = getStrategy(_signal);
	if (strategy == Strategy::DEFAULT)
	{
		auto handler = getHandler(_signal);
		std::signal(_signal, handler);

		try
		{
			std::raise(_signal);
		}
		catch (std::exception& exception)
		{
			Logger::output(Logger::Level::ERROR, \
				std::source_location::current(), \
				exception);
		}
	}
}

auto SignalManager::getHandler(int _signal) -> Handler
{
	std::lock_guard lock(_handleMutex);
	auto iterator = _handleMapper.find(_signal);
	return iterator != _handleMapper.end() ? \
		iterator->second : SIG_DFL;
}

void SignalManager::setHandler(int _signal, Handler _handler)
{
	std::lock_guard lock(_handleMutex);
	_handleMapper.insert_or_assign(_signal, _handler);
}

auto SignalManager::getStrategy(int _signal) -> Strategy
{
	std::lock_guard lock(_strategyMutex);
	auto iterator = _strategyMapper.find(_signal);
	return iterator != _strategyMapper.end() ? \
		iterator->second : Strategy::DEFAULT;
}

void SignalManager::setStrategy(int _signal, Strategy _strategy)
{
	std::lock_guard lock(_strategyMutex);
	_strategyMapper.insert_or_assign(_signal, _strategy);
}

bool SignalManager::insertObserver(int _signal, IDType _id, \
	const Observer& _observer)
{
	if (not _observer) return false;

	std::lock_guard lock(_poolMutex);
	if (auto iterator = _poolMapper.find(_signal); \
		iterator != _poolMapper.end())
	{
		iterator->second.insert_or_assign(_id, _observer);
		return true;
	}

	PoolType pool{ std::make_pair(_id, _observer) };
	_poolMapper.emplace(_signal, std::move(pool));
	std::signal(_signal, handle);
	return true;
}

bool SignalManager::insertObserver(int _signal, IDType _id, Observer&& _observer)
{
	if (not _observer) return false;

	std::lock_guard lock(_poolMutex);
	if (auto iterator = _poolMapper.find(_signal); iterator != _poolMapper.end())
	{
		iterator->second.insert_or_assign(_id, std::forward<Observer>(_observer));
		return true;
	}

	PoolType pool{ std::make_pair(_id, std::forward<Observer>(_observer)) };
	_poolMapper.emplace(_signal, std::move(pool));
	std::signal(_signal, handle);
	return true;
}

bool SignalManager::removeObserver(int _signal, IDType _id)
{
	std::lock_guard lock(_poolMutex);
	auto iterator = _poolMapper.find(_signal);
	if (iterator == _poolMapper.end()) return false;

	auto& pool = iterator->second;
	if (auto iterator = pool.find(_id); \
		iterator != pool.end())
		pool.erase(iterator);
	else return false;

	if (pool.empty())
	{
		std::signal(_signal, getHandler(_signal));
		_poolMapper.erase(iterator);
	}
	return true;
}

ETERFREE_SPACE_END
